﻿using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using xycn.framework.PluginManagement;
using xycn.pluginsupport.AspNetCorePlugins;

namespace xycn.framework.MidwareManagement
{
    /// <summary>
    /// 中间件插件加载器
    /// </summary>
    public class MidwareLoader
    {
        /// <summary>
        /// 中间件插件加载错误信息
        /// </summary>
        public static string errMsg = "";

        /// <summary>
        /// 加载中间件插件
        /// </summary>
        /// <param name="midware_path">中间件插件库所在文件夹</param>
        /// <param name="midwares">输出，中间件插件库中包含的中间件插件函数列表</param>
        /// <returns></returns>
        public static int LoadMidwares(string midware_path, out List<MidwareInfo> midwares)
        {
            int ret = 0;

            midwares = new List<MidwareInfo>();
            
            Assembly midware_assembly = null;
            try
            {
                midware_assembly = GlobalContext.LoadPlugin(midware_path);
                                
                try
                {
                    //获取dll中所有的类
                    Type[] types = midware_assembly.GetTypes();
                    foreach (Type type in types)
                    {
                        //缓存midware信息
                        MidwareInfo midware_info = new MidwareInfo();
                        midware_info.className = type.Name;

                        midware_info.midwareType = type;
                        midware_info.fromLib = midware_path;
                        midware_info.Namespace = type.Namespace;

                        if (AnalyzeMidwareMethod(midware_info) < 0)
                        {
                            ret = -1;
                        }
                        SortCallSeq(midware_info);
                        midwares.Add(midware_info);
                    }
                }
                catch (Exception ex)
                {
                    errMsg = "分析程序集：" + midware_assembly.FullName +
                        "时，出现错误," + ex.Message;
                    return -1;
                }
            }
            catch (Exception ex)
            {
                errMsg = errMsg + "[ERROR]：加载\"" + midware_path +
                    "\"时，出现错误，请确认是否为合法程序集...\r\n" + ex.Message;
                return -1;
            }
            return ret;
        }

        /// <summary>
        /// 根据调用顺序，调整中间件方法的顺序
        /// </summary>
        /// <param name="midware_info"></param>
        private static void SortCallSeq(MidwareInfo midware_info)
        {
            List<MidwareMethodInfo> temp = new List<MidwareMethodInfo>();

            //冒泡调整顺序
            foreach (MidwareMethodInfo method in midware_info.methodInfors)
            {
                bool is_insert = false;
                for (int i = 0; i < temp.Count; i ++)
                {
                    MidwareMethodInfo method2 = temp[i];
                    if (method.callSN < method2.callSN)
                    {
                        temp.Insert(i, method);
                        is_insert = true;
                        break;
                    }
                }

                if (!is_insert)
                    temp.Add(method);
            }
            midware_info.methodInfors = temp;
        }

        /// <summary>
        /// 分析中间件库，提取中间件函数
        /// </summary>
        /// <param name="midwareInfo">中间件插件信息</param>
        /// <returns></returns>
        private static int AnalyzeMidwareMethod(MidwareInfo midwareInfo)
        {
            int ret = 0;
            //创建中间件函数列表
            midwareInfo.methodInfors = new List<MidwareMethodInfo>();
            foreach (MethodInfo method in midwareInfo.midwareType.GetMethods())
            {
                MidwareMethodInfo midwaremethod_info = null;

                //判断是否存在AspNetMidware，从而确定是否为中间件处理函数
                AspNetMidware[] midware_attrs =
                    (AspNetMidware[])method.GetCustomAttributes(
                        typeof(AspNetMidware), true);
                if (midware_attrs.Count() <= 0)
                    continue;

                AspNetMidware midware_attr = (AspNetMidware)midware_attrs[0];

                if (method.ReturnType != typeof(Task))
                {
                    errMsg = errMsg + "方法名：" + method.Name + "，的返回值定义存在错误，中间件处理函数返回值必须为Task（建议使用async Task定义中间件处理函数）\r\n";
                    ret = -1;
                    continue;
                }

                midwaremethod_info = new MidwareMethodInfo();
                midwaremethod_info.callSN = midware_attr.callSN;
                midwaremethod_info.Name = method.Name;
                midwaremethod_info.methodInfor = method;

                //分析函数输入参数
                if (!isMidwareParmeter(method))
                {
                    ret = -1;
                    continue;
                }

                midwareInfo.methodInfors.Add(midwaremethod_info);
            }
            
            return ret;
        }


        /// <summary>
        /// 判断库中标记为中间件的函数，是否合规
        /// </summary>
        /// <param name="method">反射出来的函数信息</param>
        /// <returns></returns>
        private static bool isMidwareParmeter(MethodInfo method)
        {
            ParameterInfo[] parameters = method.GetParameters();
            if (parameters.Length != 2)
            {
                errMsg = errMsg + "方法名：" + method.Name + "，的入参存在错误，中间件处理函数的入参，必须为HttpContext类型和Func<Task>类型的两个参数\r\n"; 
                return false;
            }

            if (parameters[0].ParameterType != typeof(HttpContext))
            {
                errMsg = errMsg + "方法名：" + method.Name + "的入参：HttpContext类型错误，所需HttpContext类型信息如下：\r\n";
                errMsg = errMsg + "全名：" + typeof(HttpContext).Assembly.FullName + "\r\n";
                errMsg = errMsg + "依赖库路径：" + typeof(HttpContext).Assembly.Location + "\r\n";
                return false;
            }

            if (parameters[1].ParameterType != typeof(Func<Task>))
            {
                errMsg = errMsg + "方法名：" + method.Name + "的入参：Func<Task>类型错误，所需Func<Task>类型信息如下：\r\n";
                errMsg = errMsg + "全名：" + typeof(Func<Task>).Assembly.FullName + "\r\n";
                errMsg = errMsg + "依赖库路径：" + typeof(Func<Task>).Assembly.Location + "\r\n";

                return false;
            }
            return true;
        }
    }
}
